<?php

declare(strict_types=1);

namespace Core;

use InvalidArgumentException;
use RuntimeException;

/**
 * 文件日志记录器
 *
 * 该类提供了静态方法来记录不同级别的日志消息到指定文件。
 * 它被设计为不可实例化和不可继承，以确保其作为纯粹的静态工具类使用。
 *
 * 主要特性:
 * - 支持 INFO 和 ERROR 级别。
 * - 自动创建日志目录和文件。
 * - 包含时间戳和日志级别。
 * - 优化了文件写入操作，使用文件锁防止竞态条件。
 * - 灵活的日志文件路径配置。
 */
final class Logger
{
    /**
     * 日志文件的完整路径。
     * 设为私有，通过 `initialize` 方法进行配置，避免硬编码。
     * @var string|null
     */
    private static ?string $logFile = null;

    /**
     * 用于防止意外实例化的私有构造函数。
     * 该类应仅作为静态工具使用。
     */
    private function __construct()
    {
        // Private constructor to prevent instantiation.
    }

    /**
     * 记录一条 INFO 级别的消息。
     *
     * @param string $message 要记录的消息。
     * @param array<string, mixed> $context 可选的上下文数据，将被转换为 JSON 字符串。
     * @return void
     */
    public static function info(string $message, array $context = []): void
    {
        self::writeLog('INFO', $message, $context);
    }

    /**
     * 记录一条 ERROR 级别的消息。
     *
     * @param string $message 要记录的消息。
     * @param array<string, mixed> $context 可选的上下文数据，将被转换为 JSON 字符串。
     * @return void
     */
    public static function error(string $message, array $context = []): void
    {
        self::writeLog('ERROR', $message, $context);
    }

    /**
     * 获取当前日期的日志文件完整路径。
     * 该方法负责动态生成路径并确保目录存在。
     *
     * @return string 日志文件的完整路径 (e.g., /path/to/project/runtime/logs/2023-10-27.log)
     * @throws RuntimeException 如果日志目录无法创建。
     */

     private static function getLogFile(): string
     {
         $logDirectory = __DIR__ . '/../runtime/logs';
         self::ensureDirectoryExists($logDirectory);
         $filename = date('Y-m-d') . '.log';
 
         return $logDirectory . DIRECTORY_SEPARATOR . $filename;
     }

    /**
     * 将日志消息写入文件。
     * 这是一个核心的私有方法，处理所有文件操作和格式化。
     *
     * @param string $level   日志级别 (e.g., 'INFO', 'ERROR')。
     * @param string $message 日志消息。
     * @param array<string, mixed>  $context 上下文数据。
     * @return void
     * @throws RuntimeException 如果目录/文件创建失败或写入失败。
     */
    private static function writeLog(string $level, string $message, array $context): void
    {
        // 动态获取当日志文件路径
        $logFile = self::getLogFile();

        // 格式化上下文数据
        $contextString = !empty($context)
            ? ' ' . json_encode($context, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
            : '';

        // 格式化最终的日志条目
        $formattedMessage = sprintf(
            "[%s] %s: %s%s%s",
            date('Y-m-d H:i:s'), // 使用当前时间戳
            $level,
            $message,
            $contextString,
            PHP_EOL
        );

        // 安全与性能：使用文件锁 (LOCK_EX) 防止并发写入时的数据损坏
        if (file_put_contents($logFile, $formattedMessage, FILE_APPEND | LOCK_EX) === false) {
            // 在生产环境中，这里应该触发一个更高级别的警报，因为日志写入失败是严重问题
            throw new RuntimeException("未能写入日志文件: {$logFile}");
        }
    }

    /**
     * 确保指定的目录存在，如果不存在则尝试创建它。
     *
     * @param string $directoryPath 目录的路径。
     * @return void
     * @throws RuntimeException 如果目录不存在且无法创建。
     */
    private static function ensureDirectoryExists(string $directoryPath): void
    {
        if (!is_dir($directoryPath) && !mkdir($directoryPath, 0755, true) && !is_dir($directoryPath)) {
            // 安全性：使用更严格的 0755 权限
            // `!is_dir()` 检查是防止 `mkdir` 在竞态条件下返回 false 的情况
            throw new RuntimeException("创建日志目录失败: {$directoryPath}");
        }
    }
}
